
#include <functional>
#include <sstream>
#include "LogeD.h"

#ifdef ANDROID
#include <android/log.h>
#define TAG "JNI"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,TAG ,__VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,TAG ,__VA_ARGS__) 
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN,TAG ,__VA_ARGS__) 
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,TAG ,__VA_ARGS__)
#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL,TAG ,__VA_ARGS__)

#endif //ANDROID


//static int first = 0;


#ifdef ANDROID

#define mprintf LOGE
#else
#define mprintf printf
#endif



LogeD LogeD::m_oInstance;
bool LogeD::CreateDir()
{
#ifdef WIN32
	if (_access(m_LogConfig.m_logDir.c_str(), 00) != 0) {
		_mkdir(m_LogConfig.m_logDir.c_str());
#else
	if (access(m_LogConfig.m_logDir.c_str(), R_OK | W_OK) != 0)
	{
		mkdir(m_LogConfig.m_logDir.c_str(), S_IRWXU);
#endif
	}
	return 0;
}

bool LogeD::CreateFile()
{
	long long currentMs = getCurrentMs();
	char currentMsStr[256];

	sprintf(currentMsStr, "%lld", currentMs);

	std::string logPath = m_LogConfig.m_logDir + "/LOG_" + currentMsStr;

	fp = NULL;
	fp = fopen(logPath.c_str(), "w+");
	if (fp == NULL)
	{
		return false;
	}
	return true;


}

bool LogeD::CloseFile()
{
	if (fp)
	{
		fflush(fp);
		fclose(fp);
		fp = 0;

	}
	return true;
}


int LogeD::logWrite(LevelEnum eLevel, std::string & str)
{
	if (fp)
	{

		std::lock_guard<std::mutex> lk(mux_file);
		if (eLevel >= m_LogConfig.m_LevelEnum)
		{
			fprintf(fp, "%s", str.c_str());
		}

		//fwrite(str.c_str(), str.size(), 1, fp);
		fflush(fp);
		int len = ftell(fp);
		if (len > m_LogConfig.m_oneLogFileSize)
		{
			CloseFile();
			removeOldFile();
			CreateFile();
			return 0;
		}

	}

	// 写到终端
	if (m_LogConfig.m_is_WriteToTerminal)
	{
		mprintf("%s ", str.c_str());
	}

	return 0;
}


long long LogeD::getCurrentMs()
{
	auto time_now = std::chrono::system_clock::now();
	auto duration_in_ms = std::chrono::duration_cast<std::chrono::milliseconds>(time_now.time_since_epoch());

	return duration_in_ms.count();
}

std::string LogeD::GetLastName(const char* fileName)
{
	char temp[256] = { 0 };
	memcpy(temp, fileName, strlen(fileName) + 1);

	std::string sName = temp;

#ifdef WIN32
	int startP = sName.find_last_of("\\");
	if (startP == -1)
	{
		startP = 0;
	}
	std::string theLastName = sName.substr(startP + 1, sName.size() - startP);

#else
	int startP = sName.find_last_of("/");
	if (startP == -1)
	{
		startP = 0;
	}
	std::string theLastName = sName.substr(startP + 1, sName.size() - startP);

#endif
	return theLastName;
}


std::string LogeD::getCurrentSystemTime()
{
	auto time_now = std::chrono::system_clock::now();
	auto tt = std::chrono::system_clock::to_time_t(time_now);
	auto duration_in_ms = std::chrono::duration_cast<std::chrono::milliseconds>(time_now.time_since_epoch());
	auto duration_in_s = std::chrono::duration_cast<std::chrono::seconds>(time_now.time_since_epoch());
	int theMs = duration_in_ms.count() - duration_in_s.count() * 1000;
	struct tm* ptm = localtime(&tt);


	char date[60] = { 0 };
	sprintf(date, "%d-%02d-%02d-%02d.%02d.%02d.%03d%",
		(int)ptm->tm_year + 1900, (int)ptm->tm_mon + 1, (int)ptm->tm_mday,
		(int)ptm->tm_hour, (int)ptm->tm_min, (int)ptm->tm_sec, theMs);
	return std::string(date);
}

static int com(const void* a, const void* b)
{
	long long* p1, * p2;
	p1 = (long long*)a;  //
	p2 = (long long*)b;  //

	return *p1 > *p2 ? 1 : -1;
}

int LogeD::removeOldFile()
{

	std::vector<std::string> files;

	std::vector<long long> filesNum;

	//LOGEM("removeOldFile ");
	getFiles(m_LogConfig.m_logDir, files);

	//LOGEM("rootPath = %s files.size = %d \n", _logDir.c_str(), files.size());
	if (files.size() > m_LogConfig.m_maxFileSize)
	{
		for (int i = 0; i < files.size(); i++)
		{
			int pos0 = files[i].find("LOG_");

			std::string cut = files[i].substr(pos0 + 4, files[i].size());

			char* endptr = NULL;
			long long num = std::strtoll(cut.c_str(), &endptr, 10);

			//LOGEM("cut = %s num = %lld \n", cut.c_str(), num);

			filesNum.push_back(num);

		}
		qsort(&filesNum.data()[0], filesNum.size(), sizeof(long long), com);

		// 
		//LOGEM(" delete num = %d   \n", filesNum.size() - 5);
		long long deleteNum = filesNum.size() - m_LogConfig.m_maxFileSize;

		for (int i = 0; i < deleteNum; i++)
		{
			std::string logPath = m_LogConfig.m_logDir + std::string("/LOG_") + std::to_string(filesNum[i]);
			//LOGEM("删除文件= %s   \n", logPath.c_str());
			remove(logPath.c_str());
		}
	}
	return 0;
}
void LogeD::logOut(LevelEnum eLevel, std::string & str)
{
	if (m_LogConfig.m_is_Synchronize)
	{
		// 写入文件
		logWrite(eLevel, str);
	}
	else
	{
		//mTaskQueue.push_task(str);
		std::function<int(LevelEnum mLevel, std::string str)> fun = std::bind(&LogeD::logWrite, this, eLevel, str);

		mTaskQueueFun.push_task(fun);
	}

}


void LogeD::CloseLog()
{

	if (!m_LogConfig.m_is_Synchronize)
	{
		isExit = true;
		

		if (m_thread)
		{
			if (m_thread->joinable())
			{
				std::string str="exit";
				std::function<int(LevelEnum mLevel, std::string str)> fun = std::bind(&LogeD::logWrite, this, mTrace, str);

				mTaskQueueFun.push_task(fun); // 让线程跳出循环
				m_thread->join();
				delete m_thread;
				m_thread = NULL;
			}
		}

	}

	this->CloseFile();
}

void LogeD::run()
{
	printf("void LogeD::run() ... \n");
	while (!isExit)
	{
		std::function<int(LevelEnum mLevel, std::string str)> fun;

		if (mTaskQueueFun.get_task(fun))
		{
			if (fun)
			{
				fun(mTrace, "");
			}

		}
	}
}


std::string LogeD::GetThreadId()
{
	std::ostringstream oss;
	oss << std::this_thread::get_id();
	std::string stid = oss.str();
	return stid;
}

LogeD::LogeD()
{
	printf("first 0000 \n");
	m_thread = NULL;
	isExit = false;

}

LogeD& LogeD::Instance()
{
	// TODO: 在此处插入 return 语句
	//printf("LogeD & LogeD::Instance() \n");
	//static std::shared_ptr<LogeD> s_instance(new LogeD());
	//static LogeD &s_insteanc_ref = *s_instance;
	//printf("Instance ... \n");

	return m_oInstance;
}




void LogeD::SetConfig(LogConfig LogConfig_)
{

	m_LogConfig = LogConfig_;


	if (!m_LogConfig.m_logDir.empty())
	{
		if (!CreateDir())
		{
			removeOldFile();
			if (CreateFile())
			{
				printf("CreateDir succeed CreateFile succeed .... \n");
			}
		}
	}
	if (!m_LogConfig.m_is_Synchronize) // 启动线程
	{
		this->StartRun();
	}

}


void LogeD::getFiles(std::string cate_dir, std::vector<std::string> &files)
{

#ifdef WIN32

	intptr_t   hFile = 0;

	struct _finddata_t fileinfo;//    
	std::string p;
	if ((hFile = _findfirst(p.assign(cate_dir).append("\\*").c_str(), &fileinfo)) != -1)  //  
	{
		do
		{
			if ((fileinfo.attrib & _A_SUBDIR))  //  
			{
				if (strcmp(fileinfo.name, ".") != 0 && strcmp(fileinfo.name, "..") != 0)  //  
				{
					files.push_back(p.assign(cate_dir).append("\\").append(fileinfo.name));
					getFiles(p.assign(cate_dir).append("\\").append(fileinfo.name), files);

				}
			}
			else //   
			{


				files.push_back(p.assign(cate_dir).append("\\").append(fileinfo.name));
			}

		} while (_findnext(hFile, &fileinfo) == 0);

		_findclose(hFile); //  
	}
#else


	DIR* dir;
	struct dirent* ptr;
	char base[1000];

	if ((dir = opendir(cate_dir.c_str())) == NULL)
	{
		perror("Open dir error...");
		exit(1);
	}

	while ((ptr = readdir(dir)) != NULL)
	{
		if (strcmp(ptr->d_name, ".") == 0 || strcmp(ptr->d_name, "..") == 0)    ///current dir OR parrent dir
		{
			continue;
		}
		else if (ptr->d_type == 8)    ///file
		{
			//printf("d_name:%s/%s\n",basePath,ptr->d_name);
			files.push_back(ptr->d_name);
		}
		else if (ptr->d_type == 10)    ///link file
		{
			//printf("d_name:%s/%s\n",basePath,ptr->d_name);
			continue;
		}
		else if (ptr->d_type == 4)    ///dir
		{
			files.push_back(ptr->d_name);
			/*
			memset(base,'\0',sizeof(base));
			strcpy(base,basePath);
			strcat(base,"/");
			strcat(base,ptr->d_nSame);
			readFileList(base);
			*/
		}
	}
	closedir(dir);

#endif

	//排序，按从小到大排序
	//std::sort(files.begin(), files.end())

}



LogeD::~LogeD()
{
	CloseLog();
}

void LogeD::StartRun()
{

	printf("StartRun ... \n");

	if (m_thread == NULL)
	{
		m_thread = new std::thread(std::bind(&LogeD::run, this));
	}

	printf("StartRun end ... m_thread=%p \n", m_thread);

}